import json
import traceback
from collections import OrderedDict
from . import logger

__all__ = ["MoreDataNeeded", "register", "unregister", "serialize", "deserialize"]

logger = logger.get_logger(__package__)

_serializers = OrderedDict()


class MoreDataNeeded(BaseException):
    pass


def register(obj, name):
    if not (hasattr(obj, "dumps") and hasattr(obj, "loads")):
        raise Exception("No dumps and loads functions found in " + str(obj))

    logger.info("Register serializer ... ... [%s]" % name)
    _serializers[name] = obj


def unregister(name):
    if not isinstance(name, str):
        name = name.__name__
    if name in _serializers:
        del _serializers[name]


def deserialize(bytes_data, name=None):
    if name is not None:
        if name not in _serializers:
            raise Exception("No serializer '%s' found" % name)

        serializer = _serializers[name]
        return name, serializer.loads(bytes_data)
    else:
        for name in _serializers:
            serializer = _serializers[name]
            try:
                obj = serializer.loads(bytes_data)
                if not obj:
                    continue
                return name, obj
            except MoreDataNeeded:
                raise MoreDataNeeded
            except:
                continue
        raise Exception("No valid serializer for data")


def serialize(obj, name=None):
    if name:
        serializer = _serializers[name]
        if serializer:
            try:
                return serializer.dumps(obj)
            except:
                raise Exception("No valid serializer for data")

    else:
        for serializer in _serializers:
            try:
                bs = _serializers[serializer].dumps(obj)
                if not bs:
                    continue
                return bs
            except:
                traceback.print_exc()
                continue
        raise Exception("No valid serializer for data")


serializers = _serializers


class SimpleHttpParser:
    def dumps(self, obj):
        http_str = ""
        if "method" in obj:
            http_str += "%s %s %s/%s\r\n" % (obj["method"],
                                             obj["uri"],
                                             obj["protocol"],
                                             obj["version"])
        else:
            http_str += "%s/%s %s %s\r\n" % (obj["protocol"],
                                             obj["version"],
                                             str(obj["status_code"]),
                                             obj["status"])
        for key in obj["headers"].keys():
            http_str += "%s: %s\r\n" % (key, obj["headers"][key])

        http_str += "\r\n"
        if "body" in obj:
            http_str += obj["body"]

        return http_str.encode("utf-8")

    def loads(self, data):
        if isinstance(data, bytes):
            data = data.decode("utf-8")

        http_obj = {}
        content = data.split("\r\n\r\n")
        headers = {}
        http_lines = content[0].split("\r\n")
        # 解析请求行
        request = http_lines[0].split()
        if request[0] in ("GET", "POST", "HEAD", "PUT"):
            http_obj["method"] = request[0]
            http_obj["uri"] = request[1]
            protocol, version = request[2].split("/")
            http_obj["protocol"] = protocol
            http_obj["version"] = version
        else:
            protocol, version = request[0].split("/")
            http_obj["protocol"] = protocol
            http_obj["version"] = version
            http_obj["status_code"] = request[1]
            http_obj["status"] = request[2]

        # 解析头
        if len(http_lines) >= 2:
            for header in http_lines[1:]:
                if not header.strip():
                    continue

                key, value = header.split(":", 1)
                headers[key] = value.strip()
            http_obj["headers"] = headers

        # 解析文本
        if len(content) >= 3:
            http_obj["body"] = content[1]

        return http_obj


class WebSocketParser:
    def dumps(self, obj):
        if obj['opcode'] == 129:
            p = obj['data'].encode("utf-8")
            bs = bytearray()
            bs.append(129)
            if len(p) <= 125:
                bs.append(len(p))
            elif len(p) > 126:
                pass
            for b in p:
                bs.append(b)
            return bytes(bs)

    def loads(self, obj):
        assert obj[0] in (0x81, 0x88, 0x89, 0x8A)
        if obj[0] == 0x81:
            logger.info("parsing the data with websocket serializer")
            data_len = obj[1] - 0x80
            if data_len < 126:
                mask = obj[2:6]
                data = obj[6:]
            else:
                mask = obj[4:8]
                data = obj[8:]

            if data_len > len(data):
                raise MoreDataNeeded
            encoded_data = bytearray()

            for i in range(len(data)):
                encoded_data.append(data[i] ^ mask[i % 4])
            return {"opcode": 0x81,
                    "data": encoded_data}
        elif obj[0] == 0x88:
            return {"opcode": 0x88}

        #
        # import json
        # return json.loads(encoded_data.decode("utf-8"))


class JsonParser:
    def dumps(self, obj):
        from . import packet
        if isinstance(obj, packet.Packet):
            new_obj = {}
            for k in obj:
                new_obj[k] = obj[k]
            return json.dumps(new_obj).encode("utf-8")
        return json.dumps(obj).encode("utf-8")

    def loads(self, data):
        return json.loads(data)


register(SimpleHttpParser(), "http_parser")
register(JsonParser(), "json")
register(WebSocketParser(), "websocket")
